﻿package lz.controls.containers
{
	import flash.display.DisplayObject;
	import flash.display.Loader;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.HTTPStatusEvent;
	import flash.events.IOErrorEvent;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.URLRequest;
	import flash.system.ApplicationDomain;
	import flash.system.LoaderContext;
	import flash.ui.Keyboard;
	
	import lz.controls.ScrollPolicy;
	import lz.controls.core.InvalidationType;
	import lz.controls.events.ScrollEvent;
	import lz.controls.managers.IFocusManagerComponent;
    /**
     * ScrollPane 组件在一个可滚动区域中呈现显示对象和 JPEG、GIF 与 PNG 文件，以及 SWF 文件。 
	 * 可以使用滚动窗格来限制这些媒体类型所占用的屏幕区域。 
	 * 滚动窗格可以显示从本地磁盘或 Internet 加载的内容。 
	 * 在创作过程中和运行时，您都可以使用 ActionScript 设置此内容。 
	 * 如果滚动窗格的内容具有有效的 Tab 停靠位，则窗格获得焦点以后，那些标记将获得焦点。 
	 * 离开内容中的最后一个 Tab 停靠位之后，焦点将移到下一个组件。 
	 * 滚动窗格中的垂直和水平滚动条不会获得焦点。
	 * 注意：正在从不同的域或沙箱加载内容时，内容的属性可能会由于安全原因而无法访问。有关域安全如何影响加载过程的详细信息，请参阅“ProLoader 类”。
 	 * 注意：在将非常大的图像文件加载到 ScrollPane 对象中时，可能需要侦听 complete 事件，然后使用 setSize() 方法调整 ScrollPane 的大小。请参阅 complete 事件示例。
     */
	public class ScrollPane extends BaseScrollPane implements IFocusManagerComponent 
	{
        /**
         * 绝对或相对 URL（该 URL 标识要加载的 SWF 或图像文件的位置）、库中影片剪辑的类名称、对显示对象的引用或者与组件位于同一层上的影片剪辑的实例名称
         */
		protected var _source:Object = "";
        /**
         *指示当用户在滚动窗格中拖动内容时是否发生滚动
         */
		protected var _scrollDrag:Boolean = false;
        /**
         * 滚动窗格中的内容的引用的容器
         */
		protected var contentClip:Sprite;
        /**
         *加载器
         */
		protected var loader:Loader;
        /**
         * x偏移量
         */
		protected var xOffset:Number;
        /**
         * y偏移量
         */
		protected var yOffset:Number;
        /**
         * @private (protected)
         */
		protected var scrollDragHPos:Number;
        /**
         * @private (protected)
         */
		protected var scrollDragVPos:Number;
        /**
		 * 滚动窗格中的内容的引用
         */
		protected var currentContent:Object;
        /**默认样式 
         */
		private static var defaultStyles:Object = {
										upSkin:"ScrollPane_upSkin",
										disabledSkin:"ScrollPane_disabledSkin",
										focusRectSkin:null,
										focusRectPadding:null,
										contentPadding:0
										}

        /**
         * 检索当前组件的默认样式映射
         */
		public static function getStyleDefinition():Object
		{
			return mergeStyles(defaultStyles, BaseScrollPane.getStyleDefinition());
		}
        /**
         *创建新的 ScrollPane 组件实例
         */
		public function ScrollPane() 
		{
			super();
		}
        /**
		 * 获取指示当用户在滚动窗格中拖动内容时是否发生滚动
         * @default false
         */
		public function get scrollDrag():Boolean 
		{
			return _scrollDrag;
		}

        /**
		 * 设置指示当用户在滚动窗格中拖动内容时是否发生滚动
         */
		public function set scrollDrag(value:Boolean):void 
		{
			_scrollDrag = value;
			invalidate(InvalidationType.STATE);
		}

        /**
		 * 加载的进度
         * @default 0
         */
		public function get percentLoaded():Number 
		{
			if (loader != null) 
			{
				return Math.round((bytesLoaded / bytesTotal) * 100);
			} else 
			{
				return 0;
			}
		}
        /**
		 * 已加载 数
         * @default 0
         */
		public function get bytesLoaded():Number 
		{
			return (loader == null || loader.contentLoaderInfo == null) ? 0 : loader.contentLoaderInfo.bytesLoaded;
		}
        /**
		 * 总加载数
         * @default 0
         */
		public function get bytesTotal():Number 
		{
			return (loader == null || loader.contentLoaderInfo == null) ? 0 : loader.contentLoaderInfo.bytesTotal;
		}
        /**
		 * 更新面板
         */
		public function refreshPane():void 
		{
			if (_source is URLRequest) 
			{
				_source = _source.url;
			}
			source = _source;
		}
        /**
		 * 更新
         */
		public function update():void 
		{
			if(contentClip.numChildren<=0)
			{
				
				return;
			}
			var child:DisplayObject = contentClip.getChildAt(0);
			setContentSize(child.width, child.height);
		}

        /**获取显示对象
         * @default null
         */
		public function get content():DisplayObject 
		{
			var c:Object = currentContent;

			if (c is URLRequest) 
			{
				c = loader.content;
			}

			return c as DisplayObject;
		}

        /**
         *获取以下内容：绝对或相对 URL（该 URL 标识要加载的 SWF 或图像文件的位置）、库中影片剪辑的类名称、对显示对象的引用或者与组件位于同一层上的影片剪辑的实例名称
         * @default null
         */
		public function get source():Object 
		{
			return _source;
		}
        /**
		 *设置以下内容：绝对或相对 URL（该 URL 标识要加载的 SWF 或图像文件的位置）、库中影片剪辑的类名称、对显示对象的引用或者与组件位于同一层上的影片剪辑的实例名称
         */
		public function set source(value:Object):void 
		{
			clearContent();
			if (isLivePreview) { return; }
			_source = value;
			if (_source == "" || _source == null)
			{
				return;
			}
			
			currentContent = getDisplayObjectInstance(value);
			if (currentContent != null)
			{
				var child :* = contentClip.addChild(currentContent as DisplayObject);
				dispatchEvent(new Event(Event.INIT));
				update();
			} else 
			{
				load(new URLRequest(_source.toString()));
			}
		}

		/**
		 * 开始加载
         */
		public function load(request:URLRequest, context:LoaderContext=null):void 
		{
			if (context == null) 
			{
				context = new LoaderContext(false, ApplicationDomain.currentDomain);
			}
			clearContent();
			initLoader();
			currentContent = _source = request;
			loader.load(request, context);
		}

        /**
		 * 设置竖向滚动位置
         * @private (protected)
         */
		override protected function setVerticalScrollPosition(scrollPos:Number, fireEvent:Boolean=false):void
		{
			var contentScrollRect :* = contentClip.scrollRect;
			contentScrollRect.y = scrollPos;
			contentClip.scrollRect = contentScrollRect;
		}

        /**设置横向滚动位置 
         * @private (protected)
         */
		override protected function setHorizontalScrollPosition(scrollPos:Number, fireEvent:Boolean=false):void 
		{
			var contentScrollRect :* = contentClip.scrollRect;
			contentScrollRect.x = scrollPos;
			contentClip.scrollRect = contentScrollRect;
		}

        /**布局
         * @private (protected)
         */
		override protected function drawLayout():void
		{
			super.drawLayout();
			contentScrollRect = contentClip.scrollRect;
			contentScrollRect.width = availableWidth;
			contentScrollRect.height = availableHeight;
			
			contentClip.cacheAsBitmap = useBitmapScrolling;
			contentClip.scrollRect = contentScrollRect;
			contentClip.x = contentClip.y = contentPadding;
		}
        /**
         *加载完毕
         */
		protected function onContentLoad(event:Event):void 
		{
			update();
			
			//Need to reset the sizes, for scrolling purposes.
			//Just reset the scrollbars, don't redraw the entire pane.
			var availableHeight :* = calculateAvailableHeight();
			calculateAvailableSize();
			horizontalScrollBar.setScrollProperties(availableWidth, 0, (useFixedHorizontalScrolling) ? _maxHorizontalScrollPosition : contentWidth - availableWidth, availableWidth);
			verticalScrollBar.setScrollProperties(availableHeight, 0, contentHeight - availableHeight, availableHeight);
			passEvent(event);
		}

        /**
         *事件转发
         */
		protected function passEvent(event:Event):void 
		{
			dispatchEvent(event);
		}
        /**
         * 初始化加载器
         */
		protected function initLoader():void 
		{
			loader = new Loader();

			loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR,handleError,false,0,true);
			loader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR,handleError,false,0,true);
			loader.contentLoaderInfo.addEventListener(Event.OPEN,passEvent,false,0,true);
			loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS,passEvent,false,0,true);
			loader.contentLoaderInfo.addEventListener(Event.COMPLETE,onContentLoad,false,0,true);
			loader.contentLoaderInfo.addEventListener(Event.INIT,passEvent,false,0,true);
			loader.contentLoaderInfo.addEventListener(HTTPStatusEvent.HTTP_STATUS,passEvent,false,0,true);

			contentClip.addChild(loader);
		}
        /**处理滚动
         * @private (protected)
         */
		override protected function handleScroll(event:ScrollEvent):void 
		{
			passEvent(event);
			super.handleScroll(event);
		}
		/**
         * @private (protected)
		 */
		protected function handleError(event:Event):void
		{
			passEvent(event);
			clearLoadEvents();
			loader.contentLoaderInfo.removeEventListener(Event.INIT,handleInit);
		}
		
		/**
         * @private (protected)
		 */
		protected function handleInit(event:Event):void
		{
			loader.contentLoaderInfo.removeEventListener(Event.INIT,handleInit);
			passEvent(event);
			invalidate(InvalidationType.SIZE);
		}
 		/**
         * @private (protected)
		 */
        protected function clearLoadEvents():void 
		{
			loader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR,handleError);
			loader.contentLoaderInfo.removeEventListener(SecurityErrorEvent.SECURITY_ERROR,handleError);
			loader.contentLoaderInfo.removeEventListener(Event.OPEN,passEvent);
			loader.contentLoaderInfo.removeEventListener(ProgressEvent.PROGRESS,passEvent);
			loader.contentLoaderInfo.removeEventListener(HTTPStatusEvent.HTTP_STATUS,passEvent);
			loader.contentLoaderInfo.removeEventListener(Event.COMPLETE,onContentLoad);
		}
       /**拖动
         */
		protected function doDrag(event:MouseEvent):void 
		{
			var yPos :Number = scrollDragVPos-(mouseY-yOffset);
			_verticalScrollBar.setScrollPosition(yPos);
			setVerticalScrollPosition(_verticalScrollBar.scrollPosition,true);
			
			var xPos :Number = scrollDragHPos-(mouseX-xOffset);
			_horizontalScrollBar.setScrollPosition(xPos);
			setHorizontalScrollPosition(_horizontalScrollBar.scrollPosition,true);
		}

        /**开始拖动
         * @private (protected)
         */
		protected function doStartDrag(event:MouseEvent):void 
		{
			if (!enabled) { return; }
			xOffset = mouseX;
			yOffset = mouseY;
			scrollDragHPos = horizontalScrollPosition;
			scrollDragVPos = verticalScrollPosition;
			stage.addEventListener(MouseEvent.MOUSE_MOVE, doDrag, false, 0, true);
		}
        /**结束拖动
         * @private (protected)
         */
		protected function endDrag(event:MouseEvent):void
		{
			stage.removeEventListener(MouseEvent.MOUSE_MOVE, doDrag);
		}
        /**设置滚动条拖动
         * @private (protected)
         */
		protected function setScrollDrag():void 
		{
			if (_scrollDrag) 
			{
				contentClip.addEventListener(MouseEvent.MOUSE_DOWN, doStartDrag, false, 0, true);
				stage.addEventListener(MouseEvent.MOUSE_UP, endDrag, false, 0, true);
			} else
			{
				contentClip.removeEventListener(MouseEvent.MOUSE_DOWN, doStartDrag);
				stage.removeEventListener(MouseEvent.MOUSE_UP, endDrag);
				removeEventListener(MouseEvent.MOUSE_MOVE, doDrag);
			}
			contentClip.buttonMode = _scrollDrag;
		}
        /**开始布局
         * @private (protected)
         */
		override protected function draw():void 
		{
			if (isInvalid(InvalidationType.STYLES)) 
			{
				drawBackground();
			}
			if (isInvalid(InvalidationType.STATE))
			{
				setScrollDrag();
			}
			super.draw();
		}

        /**
		 * 画背景
         * @private (protected)
         */
		override protected function drawBackground():void 
		{
			var bg:DisplayObject = background;
			
			background = getDisplayObjectInstance(getStyleValue(enabled ? "upSkin" : "disabledSkin"));
			if(background == null)
			{
				background = new Sprite();
			}
			background.width = width;
			background.height = height;
			addChildAt(background,0);
			background.visible = false;
			if (bg != null && bg != background) { removeChild(bg); }
		}
        /**
         * @private (protected)
         */
		protected function clearContent():void 
		{
			if (contentClip.numChildren == 0) { return; }
			contentClip.removeChildAt(0);
			currentContent = null;
			if (loader != null) 
			{
				try
				{
					loader.close();
				} catch (e:*) {}

				try 
				{
					loader.unload();
				} catch (e:*) {}

				loader = null;
			}
		}

        /**
         * @private
         */
		override protected function keyDownHandler(event:KeyboardEvent):void
		{
			var pageSize:int = calculateAvailableHeight();
			switch (event.keyCode) {
				case Keyboard.DOWN:
					verticalScrollPosition++;
					break;
				case Keyboard.UP:
					verticalScrollPosition--;
					break;
				case Keyboard.RIGHT:
					horizontalScrollPosition++;
					break;
				case Keyboard.LEFT:
					horizontalScrollPosition--;
					break;
				case Keyboard.END:
					verticalScrollPosition = maxVerticalScrollPosition;
					break;
				case Keyboard.HOME:
					verticalScrollPosition = 0;
					break;
				case Keyboard.PAGE_UP:
					verticalScrollPosition -= pageSize;
					break;
				case Keyboard.PAGE_DOWN:
					verticalScrollPosition += pageSize;
					break;
			}
		}
        /**计算有效的高
         * @private
         */
		protected function calculateAvailableHeight():Number 
		{
			var pad:Number = Number(getStyleValue("contentPadding"));
			return height-pad*2-((_horizontalScrollPolicy == ScrollPolicy.ON || (_horizontalScrollPolicy == ScrollPolicy.AUTO && _maxHorizontalScrollPosition > 0)) ? 15 : 0);
		}

        /**初始化面板
         * @private (protected)
         */
		override protected function configUI():void 
		{
			super.configUI();
			contentClip = new Sprite();
			addChild(contentClip);
			contentClip.scrollRect = contentScrollRect;
			_horizontalScrollPolicy = ScrollPolicy.AUTO;
			_verticalScrollPolicy = ScrollPolicy.AUTO;
		}
	}
}
